package com.huc.tools.tools.rsa;

import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Author: 胡成
 * @Version: 0.0.1V
 * @Date: 2018/8/9
 * @Description: 非对称加密算法RSA算法组件
 * 非对称算法一般是用来传送对称加密算法的密钥来使用的，相对于DH算法，RSA算法只需要一方构造密钥，不需要
 * 大费周章的构造各自本地的密钥对了。DH算法只能算法非对称算法的底层实现。而RSA算法算法实现起来较为简单
 **/
public class RSACoder {
    public static final String KEY_ALGORITHM = "RSA";

    private static final int KEY_SIZE = 512;

    //公钥
    private static final String PUBLIC_KEY = "RASPublicKey";

    //私钥
    private static final String PRIVATE_KEY = "RASPrivateKey";

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 10:25
     * @Description: 初始化密钥对
     **/
    public static Map<String, Object> initKey() throws NoSuchAlgorithmException {
        //实例化生成密钥
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        //初始化密钥生成器
        keyPairGenerator.initialize(KEY_SIZE);
        //生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        //甲方公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        //甲方私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, Object> keyMap = new LinkedHashMap<>();
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 10:26
     * @Description: 私钥加密
     **/
    public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception {
        //取得私钥
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(key);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //生成私钥
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        //数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 10:30
     * @Description: 公钥加密
     **/
    public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {
        //实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公钥
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);
        //产生公钥
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        //数据加密
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 10:34
     * @Description: 私钥解密
     **/
    public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
        //取得私钥
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(key);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //生成私钥
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        //数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 10:38
     * @Description: 公钥解密
     **/
    public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception {
        //实例化公钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公钥
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        //数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 10:43
     * @Description: 取得私钥
     **/
    public static byte[] getPrivateKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return key.getEncoded();
    }

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 10:44
     * @Description: 取得公钥
     **/
    public static byte[] getPublicKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return key.getEncoded();
    }

    public static void main(String[] args) throws Exception {
        Map<String, Object> keyMap = initKey();
        //公钥
        byte[] publicKey = getPublicKey(keyMap);
        //私钥
        byte[] privateKey = getPrivateKey(keyMap);
        System.out.println("公钥：" + Base64.encodeBase64String(publicKey));
        System.out.println("私钥：" + Base64.encodeBase64String(privateKey));

        System.out.println("====================密钥对构造完成,甲方将公钥公布给乙方,开始进行数据加密传输========================");
        String str = "RSA密码交换算法";
        System.out.println("====================甲方向乙方发送加密数据=========================");
        System.out.println("原文：" + str);

        //甲方进行数据加密
        byte[] code1 = encryptByPrivateKey(str.getBytes(), privateKey);
        System.out.println("加密后的数据：" + Base64.encodeBase64String(code1));
        System.out.println("====================乙方使用甲方提供的公钥对数据进行解密====================");

        //乙方数据解密
        byte[] decode1 = decryptByPublicKey(code1, publicKey);
        System.out.println("乙方解密后的数据：" + new String(decode1));

        System.out.println("====================反向进行操作,乙方向甲方发送数据========================");
        str = "乙方向甲方发送数据RSA算法";

        System.out.println("原文："+str);
        //乙方使用公钥对数据进行加密
        byte[] code2 = encryptByPublicKey(str.getBytes(),publicKey);
        System.out.println("====================乙方使用公钥对数据进行加密=======================");
        System.out.println("乙方加密后的数据："+Base64.encodeBase64String(code2));

        System.out.println("====================乙方将数据传送给甲方=====================");
        System.out.println("====================甲方使用私钥对数据进行解密====================");

        //甲方使用私钥对数据进行解密
        byte[] decode2 = decryptByPrivateKey(code2,privateKey);
        System.out.println("甲方解密后的数据："+new String(decode2));

    }


}
